# Copyright (c) 2022 Presto Labs Pte. Ltd.
# Author: lucasyoo

import json
import ast
import os
import pathlib
import collections

from cc.appcoin2.strategy.sim_backtest import SimBacktest
from cc.appcoin2.strategy.hamm.support.util import StratConfigHamm
from cc.appcoin2.strategy.umm.support.util import random_string


class HammBacktest(SimBacktest):
  def __init__(self,
               tmp_dirname,
               analyzer_feed_machine,
               *,
               is_closing_time_infinite=None,
               vol_window_sec=None,
               vol_tick_intv_sec=None,
               init_vol=None,
               maker_fee_bps=None,
               min_spread_bps=None,
               max_spread_bps=None,
               max_lean_bps=None,
               target_ratio=None,
               reserve=None,
               is_order_decay=None,
               order_qty_mode=None,
               eta=None,
               trade_cnt=None,
               book_level=None,
               trade_window_sec=None,
               price_type=None,
               spread_control=None,
               lot_size=None,
               max_pos=None,
               min_pos=None,
               q_method=None,
               lean_method=None,
               lean_risk_averse=None,
               vol_risk_averse=None,
               lot_size_quote=None,
               sticky_bp=None,
               fade_bps_multiplier=None,
               fade_decay_sec=None,
               fill_num_hurdle=None,
               fill_window_sec=None,
               fill_price_limit_sec=None,
               ewma_alpha=None,
               min_lot_size_quote=None,
               min_bandwidth_bp=None,
               max_bandwidth_bp=None,
               layering_num=None,
               order_update_period_sec=None,
               max_posting_period_sec=None,
               agg_cooldown_period_sec=None,
               agg_order_update_period_sec=None,
               agg_sw_quote_thold=None,
               agg_lw_quote_thold=None,
               agg_sw_fill_sec=None,
               agg_lw_fill_sec=None,
               agg_qty_thold=None,
               agg_update_intv_sec=None,
               agg_midp_ret_thold_bp=None,
               agg_vol_thold=None,
               agg_midp_window_sec=None,
               agg_vol_window_sec=None,
               agg_skip_qty_thold=None,
               agg_skip_quote_thold=None,
               agg_max_consecutive_skip=None,
               is_aggressive=None,
               agg_pricing_diff_thold=None,
               pick_symbol_regex=None,
               worker=None,
               machine=None,
               **kwargs):
    super().__init__(tmp_dirname, analyzer_feed_machine, **kwargs)
    self.params = {
        "is_closing_time_infinite": is_closing_time_infinite,
        "vol_window_sec": vol_window_sec,
        "vol_tick_intv_sec": vol_tick_intv_sec,
        "price_type": price_type,
        "spread_control": spread_control,
        "maker_fee_bps": maker_fee_bps,
        "min_spread_bps": min_spread_bps,
        "max_spread_bps": max_spread_bps,
        "target_ratio": target_ratio,
        "reserve": reserve,
        "is_order_decay": is_order_decay,
        "order_qty_mode": order_qty_mode,
        "eta": eta,
        "trade_cnt": trade_cnt,
        "book_level": book_level,
        "trade_window_sec": trade_window_sec,
        "lot_size": lot_size,
        "max_pos": max_pos,
        "min_pos": min_pos,
        "init_vol": init_vol,
        "q_method": q_method,
        "sticky_bp": sticky_bp,
        "lean_method": lean_method,
        "max_lean_bps": max_lean_bps,
        "lean_risk_averse": lean_risk_averse,
        "vol_risk_averse": vol_risk_averse,
        "lot_size_quote": lot_size_quote,
        "fade_bps_multiplier": fade_bps_multiplier,
        "fade_decay_sec": fade_decay_sec,
        "fill_num_hurdle": fill_num_hurdle,
        "fill_window_sec": fill_window_sec,
        "fill_price_limit_sec": fill_price_limit_sec,
        "ewma_alpha": ewma_alpha,
        "min_lot_size_quote": min_lot_size_quote,
        "min_bandwidth_bp": min_bandwidth_bp,
        "max_bandwidth_bp": max_bandwidth_bp,
        "layering_num": layering_num,
        "order_update_period_sec": order_update_period_sec,
        "max_posting_period_sec": max_posting_period_sec,
        "agg_cooldown_period_sec": agg_cooldown_period_sec,
        "agg_order_update_period_sec": agg_order_update_period_sec,
        "agg_sw_quote_thold": agg_sw_quote_thold,
        "agg_lw_quote_thold": agg_lw_quote_thold,
        "agg_sw_fill_sec": agg_sw_fill_sec,
        "agg_lw_fill_sec": agg_lw_fill_sec,
        "agg_qty_thold": agg_qty_thold,
        "agg_update_intv_sec": agg_update_intv_sec,
        "agg_midp_ret_thold_bp": agg_midp_ret_thold_bp,
        "agg_vol_thold": agg_vol_thold,
        "agg_midp_window_sec": agg_midp_window_sec,
        "agg_vol_window_sec": agg_vol_window_sec,
        "agg_skip_qty_thold": agg_skip_qty_thold,
        "agg_skip_quote_thold": agg_skip_quote_thold,
        "agg_max_consecutive_skip": agg_max_consecutive_skip,
        "is_aggressive": is_aggressive,
        "agg_pricing_diff_thold": agg_pricing_diff_thold
    }
    self.params = {k: self.params[k] for k in sorted(self.params)}
    self.pick_symbol_regex = pick_symbol_regex
    self.worker = worker or 0
    self.params.update({'config_file': self.config_path})
    self.strat_prefix = self.get_prefix(self.params)
    self.sim_binary = '../coin_binary/coin2-bazel-bin/cc/appcoin2/strategy/hamm/hamm_sim_runner'

    self.driver_config_filenames = {}
    self.strat_config_filename = {}
    self.hamm_strat_config = None

  def get_prefix(self, params):
    ignore_list = [
        'price_push_bp',
        'price_pull_bp',
        'force_fire_cooldown_sec',
        # 'sticky_bp',
        'thrift_ignore_bp',
        'thrift_ignore_levels',
        'use_agg',
        'ohlc_window_size_sec',
        'ohlc_max_len',
    ]
    tparams = collections.defaultdict(list)
    for key, val in params.items():
      if val is None:
        continue

      if key in ignore_list:
        continue

      if isinstance(val, bool):
        val = int(val)

      if isinstance(val, float):
        val = str(round(val, 9))

      if key == 'config_file':
        # val = val.replace('/', '_').replace('.', '_')
        val = val.split('/')[-1].replace('.', '_').replace('_json', '')

      key = ''.join([e[0] for e in key.split('_')])
      tparams[key].append(val)

    str_list = []
    for key in sorted(tparams.keys()):
      val = tparams[key]
      assert isinstance(val, list)
      assert len(val) > 0
      if len(val) == 1:
        str_list.append(key)
        str_list.append(str(val[0]))
      else:
        for n, v in enumerate(val):
          str_list.append(key + str(n))
          str_list.append(str(v))
    return '_'.join(str_list)

  def cmd_prerun_on_date(self, trading_interval, i):

    st = trading_interval[0].strftime('%Y%m%dT%H%M%S')
    et = trading_interval[1].strftime('%Y%m%dT%H%M%S')

    new_driver_filename = f'{self.sim_result_dir}/drivers/{self.strat_prefix}_{random_string(6)}.json'
    new_config_filename = f'{self.sim_result_dir}/configs/{self.strat_prefix}.json'

    self.driver_config_filenames[i] = new_driver_filename
    self.strat_config_filename[i] = new_config_filename
    cmd = f"""./pyrunner  \
      cc/experimental/lucasyoo/config_gen.py  \
      --analyzer_feed_machine={self.analyzer_feed_machine}  \
      --config_path={self.config_path} \
      --is_sim={True} \
      --driver_file_name={new_driver_filename} \
      --config_file_name={new_config_filename} \
      --start_dt={st} \
      --end_dt={et} \
      --params={"'" + json.dumps(ast.literal_eval(str(self.params))) + "'"} \
      --tmp_dirname={self.tmp_dirname} \
      --sim_result_dir={self.sim_result_dir} $> /dev/null"""
    print(cmd)
    return cmd

  def cmd_run_on_date(self, trading_interval, i):
    if self.config_path is None:
      return

    from_dt_str = trading_interval[0].strftime("%Y%m%dT%H%M%S")

    cmd_sim = f"""{self.sim_binary} \
      --driver={self.driver_config_filenames[i]} \
      --config={self.strat_config_filename[i]} """
    cmd_sim += f"""|& (head -n 500; tail -n 500) &>> {self.tmp_dirname}/log{from_dt_str}.txt""" if os.environ.get(
        f'SHORT_LOG') else f"""&>> {self.tmp_dirname}/log{from_dt_str}.txt"""

    print(cmd_sim)
    cmdpath = pathlib.Path(f'{self.sim_result_dir}/log_runsim_cmd.txt')
    if cmdpath.is_file():
      content = cmdpath.read_text() + cmd_sim + '\n'
    else:
      content = cmd_sim + '\n'
    cmdpath.write_text(content)
    return cmd_sim